package com.g360.sys.util;

/**
 * Created by vpc on 2015/10/27.
 */

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.security.crypto.bcrypt.BCrypt;

/**
 * Author: Ian Gallagher <igallagher@securityinnovation.com>
 *
 * This code utilizes jBCrypt, which you need installed to use.
 * jBCrypt: http://www.mindrot.org/projects/jBCrypt/
 */

public class Password {

    private static final Logger LOGGER =
            LoggerFactory.getLogger(Password.class);

    private Password(){
        
    }

    /**
     * This method can be used to generate a string representing an account password
     * suitable for storing in a database. It will be an OpenBSD-style crypt(3) formatted
     * hash string of length=60
     * The bcrypt workload is specified in the above static variable, a value from 10 to 31.
     * A workload of 12 is a very reasonable safe default as of 2013.
     * This automatically handles secure 128-bit salt generation and storage within the hash.
     * @param passwordPlaintext The account's plaintext password as provided during account creation,
     *			     or when changing an account's password.
     * @return String - a string of length 60 that is the bcrypt hashed password in crypt(3) format.
     */
    public static String hashPassword(String passwordPlaintext) {
        int workload = 12;
        String salt = BCrypt.gensalt(workload);

        return BCrypt.hashpw(passwordPlaintext, salt);
    }

    /**
     * This method can be used to verify a computed hash from a plaintext (e.g. during a login
     * request) with that of a stored hash from a database. The password hash from the database
     * must be passed as the second variable.
     * @param passwordPlaintext The account's plaintext password, as provided during a login request
     * @param storedHash The account's stored password hash, retrieved from the authorization database
     * @return boolean - true if the password matches the password of the stored hash, false otherwise
     */
    public static boolean checkPassword(String passwordPlaintext, String storedHash) {
        boolean passwordVerified = false;

        if(null == storedHash || !storedHash.startsWith("$2a$")) {
            throw new IllegalArgumentException("Invalid hash provided for comparison");
        }

        passwordVerified = BCrypt.checkpw(passwordPlaintext, storedHash);

        return passwordVerified;
    }

    /**
     * A simple test case for the main method, verify that a pre-generated test hash verifies successfully
     * for the password it represents, and also generate a new hash and ensure that the new hash verifies
     * just the same.
     */
    public static void main(String[] args) {
        String testPasswd = "ichujian";
        String testHash = "$2a$12$wDYIs0Ljk4FN3/FrB8I1O.J0qIZxY45GUqne9PMWpthJdJMGGjXZS";

        LOGGER.info("Testing BCrypt Password hashing and verification");
        LOGGER.info("Test password: " + testPasswd);
        LOGGER.info("Test stored hash: " + testHash);
        LOGGER.info("Hashing test password...");

        String computedHash = hashPassword(testPasswd);
        LOGGER.info("Test computed hash: " + computedHash);
        LOGGER.info("Verifying that hash and stored hash both match for the test password...");

        String passwordsMatch = "Passwords Match";
        String passwordsDoNotMatch = "Passwords do not match";

        String compareTest = checkPassword(testPasswd, testHash)
                ? passwordsMatch : passwordsDoNotMatch;
        String compareComputed = checkPassword(testPasswd, computedHash)
                ? passwordsMatch : passwordsDoNotMatch;

        LOGGER.info("Verify against stored hash:   " + compareTest);
        LOGGER.info("Verify against computed hash: " + compareComputed);
    }

}